/*
 * poligono_monotono.c
 *
 *  Created on: Nov 2, 2012
 *      Author: Carlos Alegría Galicia
 */

#include <stdlib.h>
#include <math.h>
#include "poligono_monotono.h"

#define DISTANCIA(p, q)	sqrt(powf(p1->x - p2->x, 2) + powf(p1->y- p2->y, 2))

// calcula la distancia euclidiana entre los puntos especificados
//
float
calcular_distancia(punto *p1, punto *p2);

// calcula el área del trapecio especificado por el segmento que une a p1 y p2,
// sus proyecciones ortogonales sobre la recta y = lineaInferior, y el segmento
// de la recta anterior acotado por p1->x, y p2->x.
//
float
calcular_area_trapecio(punto *p1, punto *p2, int lineaInferior);

// Implementación de la función pm_crear_poligono
//
poligono_monotono *
pm_crear_poligono(punto **vertices, int n)
{
  //
  // verificando vértices
  //

  // apuntador no nulo
  //
  if (!vertices)
    {
      return NULL ;
    }

  // un polígono tiene al menos tres vértices
  //
  if (n < 3)
    {
      return NULL ;
    }

  // ninguno de los vértices en el arreglo debe ser nulo
  //
  int i;
  for (i = 0; i < n; i++)
    {
      if (vertices[i] == NULL)
        {
          return NULL;
        }
    }

  // verificando monotonía
  //
  for (i = 0; i < n - 1; i++)
    {
      if (vertices[i]->x >= vertices[i + 1]->x)
        break;
    }
  for (; i < n - 1; i++)
    {
      if (vertices[i]->x <= vertices[i + 1]->x)
        {
          return NULL ;
        }
    }

  //
  // calculando perímetro
  //

  float perimetro = 0;
  for (i = 0; i < n - 1; i++)
    {
      perimetro += calcular_distancia(vertices[i], vertices[i + 1]);
    }
  perimetro += calcular_distancia(vertices[n - 1], vertices[0]);

  //
  // calculando área
  //

  float area = 0;

  // calculando la posición de la línea inferior
  //
  int lineaInferior = vertices[0]->y;
  for (i = 1; i < n; i++)
    {
      if (vertices[i]->y < lineaInferior)
        {
          lineaInferior = vertices[i]->y;
        }
    }

  // cadena superior
  //
  for (i = 0; i < n - 1; i++)
    {
      if (vertices[i]->x > vertices[i + 1]->x)
        {
          break;
        }
      area += calcular_area_trapecio(vertices[i], vertices[i + 1], lineaInferior);
    }

  // cadena inferior
  //
  for (; i < n - 1; i++)
    {
      area -= calcular_area_trapecio(vertices[i + 1], vertices[i], lineaInferior);
    }
  area -= calcular_area_trapecio(vertices[0], vertices[n - 1], lineaInferior);

  poligono_monotono *p = malloc(sizeof(poligono_monotono));
  *p = (poligono_monotono) { n, vertices, perimetro, area };

  return p;
}

// implementación de la función pm_obtener_posicion
//
//
punto *
pm_obtener_posicion(poligono_monotono *poligono)
{
  if (!poligono)
    {
      return NULL ;
    }
  return poligono->vertices[0];
}

// implementación de la función pm_cambiar_posicion
//
//
void
pm_cambiar_posicion(poligono_monotono *poligono, punto *posicion)
{
  if (!poligono || !posicion)
    {
      return;
    }

  // todos los vértices deben ser movidos la misma distancia y en la misma
  // dirección que la posición del polígono
  //
  int dx = posicion->x - poligono->vertices[0]->x;
  int dy = posicion->y - poligono->vertices[0]->y;

  int i;
  for (i = 0; i < poligono->n; i++)
    {
      poligono->vertices[i]->x += dx;
      poligono->vertices[i]->y += dy;
    }
}

// Implementación de la función calcular_distancia
//
float
calcular_distancia(punto *p1, punto *p2)
{
  return sqrt(powf(p1->x - p2->x, 2) + powf(p1->y - p2->y, 2));
}

// Implementación de la función calcular_area_trapecio
//
float
calcular_area_trapecio(punto *p1, punto *p2, int lineaInferior)
{
  int a = p1->y - lineaInferior;
  int b = p2->y - lineaInferior;
  int h = p2->x - p1->x;

  return 0.5f * (a + b) * h;
}
